from flask import Flask, render_template, send_from_directory
import os
import re
import subprocess
import sys
from datetime import datetime, timedelta
from apscheduler.schedulers.background import BackgroundScheduler
import atexit  # 用于程序退出时关闭调度器

app = Flask(__name__)

# ---------------------- 配置参数（填写相对路径即可） ----------------------
# 相对路径：相对于当前脚本（flask_picture.py）所在的目录
# 例如：如果图片存在于 "当前脚本目录/data/因子复盘图表/"，则填 "data/因子复盘图表/"
RELATIVE_CHART_PATH = "数据存储路径/因子复盘图表/"  # 替换为你的实际相对路径
RELATIVE_MAKE_FACTOR_PATH = "数据存储路径/factor_maker.py"  # 脚本相对路径
RELATIVE_FUPAN_PATH = "数据存储路径/zd_fupan.py"  # 脚本相对路径
RELATIVE_DATA_PATH = "数据存储路径/get_data.py"  # 脚本相对路径

# 自动转换为绝对路径（核心修复：解决路径校验问题）
CHART_SAVE_PATH = os.path.abspath(RELATIVE_CHART_PATH)
MAKE_FACTOR_PATH = os.path.abspath(RELATIVE_MAKE_FACTOR_PATH)
FUPAN_PATH = os.path.abspath(RELATIVE_FUPAN_PATH)
DATA_PATH = os.path.abspath(RELATIVE_DATA_PATH)
# --------------------------------------------------------------------------

# 确保图表路径存在
os.makedirs(CHART_SAVE_PATH, exist_ok=True)

# ---------------------- 定时任务相关 ----------------------
#run_getdata_script

def run_getdata_script():
    """执行getdata.py脚本"""
    try:
        print(f"\n【定时任务】开始执行getdata.py，时间：{datetime.now()}")
        
        if not os.path.exists(DATA_PATH):
            print(f"【错误】未找到脚本文件：{DATA_PATH}")
            return
        
        # 执行脚本（捕获输出，超时1小时）
        result = subprocess.run(
            [sys.executable, DATA_PATH],
            capture_output=True,
            text=True,
            timeout=3600
        )
        
        # 输出执行结果
        if result.returncode == 0:
            print(f"【成功】getdata.py执行完成，输出：\n{result.stdout}")
        else:
            print(f"【错误】getdata.py执行失败，返回码：{result.returncode}")
            print(f"错误输出：\n{result.stderr}")
            
    except Exception as e:
        print(f"【异常】执行getdata.py时出错：{str(e)}")

def run_zd_fupan_script():
    """执行zd_fupan.py脚本"""
    try:
        print(f"\n【定时任务】开始执行zd_fupan.py，时间：{datetime.now()}")
        
        if not os.path.exists(FUPAN_PATH):
            print(f"【错误】未找到脚本文件：{FUPAN_PATH}")
            return
        
        # 执行脚本（捕获输出，超时1小时）
        result = subprocess.run(
            [sys.executable, FUPAN_PATH],
            capture_output=True,
            text=True,
            timeout=3600
        )
        
        # 输出执行结果
        if result.returncode == 0:
            print(f"【成功】zd_fupan.py执行完成，输出：\n{result.stdout}")
        else:
            print(f"【错误】zd_fupan.py执行失败，返回码：{result.returncode}")
            print(f"错误输出：\n{result.stderr}")
            
    except Exception as e:
        print(f"【异常】执行zd_fupan.py时出错：{str(e)}")


def run_make_factor_script():
    """执行factor_maker.py脚本"""
    try:
        print(f"\n【定时任务】开始执行factor_maker.py，时间：{datetime.now()}")
        
        if not os.path.exists(MAKE_FACTOR_PATH):
            print(f"【错误】未找到脚本文件：{MAKE_FACTOR_PATH}")
            return
        
        result = subprocess.run(
            [sys.executable, MAKE_FACTOR_PATH],
            capture_output=True,
            text=True,
            timeout=3600
        )
        
        if result.returncode == 0:
            print(f"【成功】factor_maker.py执行完成，输出：\n{result.stdout}")
        else:
            print(f"【错误】factor_maker.py执行失败，返回码：{result.returncode}")
            print(f"错误输出：\n{result.stderr}")
            
    except Exception as e:
        print(f"【异常】执行factor_maker.py时出错：{str(e)}")


def init_scheduler():
    """初始化定时任务调度器（避免debug模式重复启动）"""
    if not hasattr(app, 'scheduler'):
        app.scheduler = BackgroundScheduler(timezone="Asia/Shanghai")
        
        # 添加任务：每天15:10执行factor_maker.py，15:15执行zd_fupan.py
        app.scheduler.add_job(func=run_make_factor_script,trigger="cron",hour=15,minute=10,second=0,id="factor_maker_job")

        app.scheduler.add_job(func=run_zd_fupan_script,trigger="cron",hour=15,minute=15,second=0,id="zd_fupan_job")

        app.scheduler.add_job(func=run_getdata_script,trigger="cron",hour=15,minute=7,second=0,id="getdata_job")

        app.scheduler.start()

        print(f"【调度器】已启动，每天15:10执行factor_maker.py，15:15执行zd_fupan.py")
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print("\n", current_time)
        print(f"(----------------------分割线（1分钟频率）----------------------)")
        
        # 注册程序退出时关闭调度器
        atexit.register(lambda: app.scheduler.shutdown())

# --------------------------------------------------------------------------

# 查找最新的图表文件
def get_latest_chart():
    try:
        print(f"\n【调试】图表目录绝对路径：{CHART_SAVE_PATH}")  # 已转换为绝对路径
        
        if not os.path.exists(CHART_SAVE_PATH):
            print(f"【错误】路径不存在：{CHART_SAVE_PATH}")
            return None
        
        all_files = os.listdir(CHART_SAVE_PATH)
        print(f"【调试】目录下所有文件：{all_files}")
        
        # 匹配图片文件（正则已确认正确）
        files = [
            f for f in all_files
            if f.endswith('.png') and re.search(r'_复盘\d+天前因子对比图\.png$', f)
        ]
        print(f"【调试】匹配到的图片文件：{files}")
        
        if not files:
            return None
        
        files.sort(reverse=True)
        return files[0]
    except Exception as e:
        print(f"【错误】获取图片失败: {e}")
        return None


#############################################################################
# 图表展示页面
@app.route('/picture')
def show_picture():
    latest_chart = get_latest_chart()
    
    if not latest_chart:
        return render_template('error.html')
    
    # 提取并格式化图片日期
    date_match = re.search(r'^(\d{8})_', latest_chart)
    date_str = date_match.group(1) if date_match else "未知日期"
    try:
        formatted_date = datetime.strptime(date_str, "%Y%m%d").strftime("%Y年%m月%d日")
    except:
        formatted_date = date_str
    
    return render_template(
        'picture.html',
        latest_chart=latest_chart,
        formatted_date=formatted_date,
        current_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        next_refresh=(datetime.now() + timedelta(seconds=100)).strftime("%Y-%m-%d %H:%M:%S")
    )

#############################################################################
# 图表展示页面
@app.route('/index') 
def picture_index():
    
    latest_chart = get_latest_chart()
    if not latest_chart:
        return render_template('error.html')
    
    # 提取并格式化图片日期
    date_match = re.search(r'^(\d{8})_', latest_chart)
    date_str = date_match.group(1) if date_match else "未知日期"
    try:
        formatted_date = datetime.strptime(date_str, "%Y%m%d").strftime("%Y年%m月%d日")
    except:
        formatted_date = date_str
    
    return render_template(
        'index.html',
        latest_chart=latest_chart,
        formatted_date=formatted_date,
        current_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        next_refresh=(datetime.now() + timedelta(seconds=100)).strftime("%Y-%m-%d %H:%M:%S")
    )


# 提供图片文件访问（修复路径校验逻辑）
@app.route('/chart/<filename>')
def get_chart(filename):
    # 拼接图片绝对路径
    full_path = os.path.join(CHART_SAVE_PATH, filename)
    full_path_abs = os.path.abspath(full_path)  # 确保是绝对路径
    
    print(f"【调试】图片绝对路径：{full_path_abs}")
    print(f"【调试】图表目录绝对路径：{CHART_SAVE_PATH}")
    
    # 安全校验：用绝对路径前缀匹配（核心修复）
    if not full_path_abs.startswith(CHART_SAVE_PATH):
        print("【错误】路径遍历攻击检测：非法路径")
        return "无效的文件路径", 403
    
    # 检查文件是否存在
    if not os.path.exists(full_path_abs):
        print(f"【错误】文件不存在：{full_path_abs}")
        return "文件不存在", 404
    
    return send_from_directory(CHART_SAVE_PATH, filename)

if __name__ == '__main__':
    init_scheduler()
    # 生产环境需关闭debug，且注意80端口可能需要管理员权限
    app.run(debug=True, host='0.0.0.0', port=80)